package com.demo2do.boyle.common.entity.system;

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;

import com.demo2do.core.security.details.SecurityUserDetails;
import com.demo2do.core.utils.SignatureUtils;

/**
 * 
 * @author downpour
 */
@Entity
@Table(name = "principal")
public class Principal implements SecurityUserDetails {
    
    private static final long serialVersionUID = 3459671975950850048L;

    @Id
    @GeneratedValue
    private Integer id;
    
    private String name;
    
    private String password;
    
    private String authority;
    
    private String mobile;
    
    @Column(name = "is_deleted")
    private boolean disabled;
    
    @Transient
    private Map<String, Set<String>> resources = new HashMap<String, Set<String>>();
    
    /**
     * The default constructor
     */
    public Principal() {
        
    }

    /**
     * 
     * @param name
     * @param password
     * @param authority
     */
    public Principal(String name, String password, String authority){
        this.name = name;
        this.password = password;
        this.authority = authority;
    }
    
    /**
     * Initialize principal basic fields
     * 
     * @return
     */
    public Principal initialize() {
        
    	// using mobile's last 6 digitals as password 
    	this.resetPassword();
    	
        return this;
    }
    
    /**
     * 
     * @return
     */
    public Principal resetPassword() {
    	this.password = SignatureUtils.makeMD5Signature(this.mobile.substring(this.mobile.length() - 6));
        return this;
    }
    
    /**
     * Initialize resources
     * 
     * @param resources
     */
    public void initResources(Map<String, List<String>> resources) {
        
        for(Iterator<Map.Entry<String, List<String>>> iterator = resources.entrySet().iterator(); iterator.hasNext();) {
            Map.Entry<String, List<String>> entry = iterator.next();
            this.addResource(entry.getKey(), entry.getValue());
        }

    }
    
    /**
     * Add resource from key and value
     * 
     * @param key
     * @param values
     */
    public void addResource(String key, List<String> values) {
        if(this.resources.containsKey(key)) {
            this.resources.get(key).addAll(values);
        } else {
            this.resources.put(key, new LinkedHashSet<String>(values));
        }
    }
    
    /**
     * Get resource from key
     * 
     * @param key
     * @return
     */
    @Transient
    public Set<String> getResource(String key) {
        return this.resources.get(key);
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.core.security.details.SecurityUserDetails#hasResource(java.lang.String, java.lang.String)
     */
    public boolean hasResource(String type, String key) {
    	return this.resources.containsKey(type) && this.resources.get(type).contains(key);
    }
    
    /* (non-Javadoc)
     * @see com.demo2do.core.security.details.SecurityUserDetails#hasAnyPrincipalRole(java.lang.String[])
     */
    public boolean hasAnyPrincipalRole(String... roles) {
    	
    	Set<String> roleSet = AuthorityUtils.authorityListToSet(this.getAuthorities());

        for (String role : roles) {
            if (roleSet.contains(role)) {
                return true;
            }
        }
        
        return false;
    }

	/* (non-Javadoc)
	 * @see org.springframework.security.core.userdetails.UserDetails#getAuthorities()
	 */
	public Collection<? extends GrantedAuthority> getAuthorities() {
		return AuthorityUtils.commaSeparatedStringToAuthorityList(this.authority);
	}

	/* (non-Javadoc)
	 * @see org.springframework.security.core.userdetails.UserDetails#getPassword()
	 */
	public String getPassword() {
		return this.password;
	}

	/* (non-Javadoc)
	 * @see org.springframework.security.core.userdetails.UserDetails#getUsername()
	 */
	public String getUsername() {
		return this.mobile;
	}

	/* (non-Javadoc)
	 * @see org.springframework.security.core.userdetails.UserDetails#isAccountNonExpired()
	 */
	public boolean isAccountNonExpired() {
		return true;
	}

	/* (non-Javadoc)
	 * @see org.springframework.security.core.userdetails.UserDetails#isAccountNonLocked()
	 */
	public boolean isAccountNonLocked() {
		return true;
	}

	/* (non-Javadoc)
	 * @see org.springframework.security.core.userdetails.UserDetails#isCredentialsNonExpired()
	 */
	public boolean isCredentialsNonExpired() {
		return true;
	}

	/* (non-Javadoc)
	 * @see org.springframework.security.core.userdetails.UserDetails#isEnabled()
	 */
	public boolean isEnabled() {
		return true;
	}

	/**
	 * @return the id
	 */
	public Integer getId() {
		return id;
	}

	/**
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	/**
	 * @return the authority
	 */
	public String getAuthority() {
		return authority;
	}

	/**
	 * @return the mobile
	 */
	public String getMobile() {
		return mobile;
	}

	/**
	 * @return the resources
	 */
	public Map<String, Set<String>> getResources() {
		return resources;
	}

	/**
	 * @param id the id to set
	 */
	public void setId(Integer id) {
		this.id = id;
	}

	/**
	 * @param name the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * @param password the password to set
	 */
	public void setPassword(String password) {
		this.password = password;
	}

	/**
	 * @param authority the authority to set
	 */
	public void setAuthority(String authority) {
		this.authority = authority;
	}

	/**
	 * @param mobile the mobile to set
	 */
	public void setMobile(String mobile) {
		this.mobile = mobile;
	}

	/**
	 * @param resources the resources to set
	 */
	public void setResources(Map<String, Set<String>> resources) {
		this.resources = resources;
	}

	/**
	 * @return the disabled
	 */
	public boolean isDisabled() {
		return disabled;
	}

	/**
	 * @param disabled the disabled to set
	 */
	public void setDisabled(boolean disabled) {
		this.disabled = disabled;
	}

	
}
